import struct ,zlib,itertools,pyexiv2 ,os ,re ,binascii ,math ,subprocess
from PIL import Image , ImageSequence
from tools import config



def 图片元数据(self):
    
    try:
        img = pyexiv2.Image(config.file_name, encoding='GBK')
        exif = img.read_exif()
        self.ui.print_echo.append('[*] 图片元数据 - EXIF信息：')
        命令=[
            ("高度" ,exif['Exif.Image.ImageWidth']),("宽度" , exif['Exif.Image.ImageLength']),
            ("拍摄时间", exif['Exif.Photo.DateTimeOriginal']),("写入时间",exif['Exif.Photo.DateTimeDigitized']),
            ("秒时间",exif['Exif.Photo.SubSecTime']),("GPS时间", exif['Exif.GPSInfo.GPSDateStamp']),
            ("拍摄设备制造商", exif['Exif.Image.Make']),("拍摄设备软件", exif['Exif.Image.Software']),
            ("纬度N",".".join(exif['Exif.GPSInfo.GPSLatitude'].split('/1 ',3)).split('/')[0]),
            ("经度E",".".join(exif['Exif.GPSInfo.GPSLongitude'].split('/1 ',3)).split('/')[0]),
            ("海拔",exif['Exif.GPSInfo.GPSAltitude'])]
        for key, value in 命令:
            self.ui.print_echo.append('{}:{}'.format(key, value))
        iptc = img.read_iptc()
        self.ui.print_echo.append('[*] 图片元数据 - IPTC信息：')
        for key, value in iptc.items():
            print('{}:{}'.format(key, value))
            self.ui.print_echo.append('{}:{}'.format(key, value))
        xmp = img.read_xmp()
        self.ui.print_echo.append('[*] 图片元数据 - XMP信息：')
        for key, value in xmp.items():
            print('{}:{}'.format(key, value))
            self.ui.print_echo.append('{}:{}'.format(key, value))
        img.close()
    except:
        self.ui.print_echo.append('[*] 图片元数据 - 读取失败或文件没有元数据')
    config.清理目录(self)

def 盲水印(self):
    
    原图=config.str1
    加密图=config.str2
    try: # 检测是否输入了路径
        if not 原图:
            self.ui.print_echo.append("请在变量1中输入原图路径,路径不能有中文")
        if not 加密图:
            self.ui.print_echo.append("请在变量2中输入加密图路径,路径不能有中文")
    except:
        pass
    try:
        保存路径 = os.path.split(原图)[0]
        os.system(f'python ./tools/jiaoben/bwmforpy3.py decode {原图} {加密图} {保存路径+"/decode.png"} --oldseed')
        self.ui.print_echo.append("<p><img src=" + 保存路径+"/decode.png" + "/></p>")
    except:
        self.ui.print_echo.append("图片路径错误,路径不能有中文")
    config.清理目录(self)

def bin_image(self):  # 二进制转图片
    try:
        data = config.文件读取(self).decode()
        x = y = int(len(data) ** 0.5)  # 定义二维码的长短
        # print('\n')
        self.ui.print_echo.append('图片大小为:{}*{}'.format(x,y))
        rgb=[]
        for i in range(0,len(data)):  # 为了保证有rgb颜色,所以只能拓展了
            if data[i] == '0' :
                rgb.append(255)
                rgb.append(255)
                rgb.append(255)
            elif data[i] == '1':
                rgb.append(0)
                rgb.append(0)
                rgb.append(0)
    except:
        self.ui.print_echo.append("文件读取失败")
    制图(self,x,y,rgb)

def RGB转图片(self):
    
    filedata = config.文件读取(self)
    rgb_list=re.findall(r'\d+',''.join(filedata.decode()))
    try:
        rgb=[]
        col=0
        for i in rgb_list:
            if i == '0' or i == '255':
                if i == '0':
                    rgb.append('000')
                else:
                    rgb.append(i)
            else:
                rgb.append(i)
                col=1
    except:
        self.ui.print_echo.append("文件读取失败")
    try:
        if col == 1:
            num=int(len(rgb_list)/3)
            factor = []
            while num > 1:
                for i in range(num - 1):
                    k = i + 2
                    if num % k == 0:
                        factor.append(k)
                        num = int(num / k)
            self.ui.print_echo.append('图片大小为:{}*{}'.format(factor[2],factor[1]*factor[0]))
            制图(self,factor[2],factor[1]*factor[0],rgb)
        else:
            x = y = int(len(rgb_list)**0.5)
            self.ui.print_echo.append('图片大小为:{}*{}'.format(x,y))
            rgb2=[]
            for i in rgb:
                rgb2.append(i)
                rgb2.append(i)
                rgb2.append(i)
            制图(self,x,y,rgb2)
    except:
        self.ui.print_echo.append("图片分析失败")
    config.清理目录(self)

def 制图(self,x,y,rgb):
    self.ui.print_echo.append('正在生成图片,请稍后...')
    try:
        im = Image.new("RGB",(x,y))#创建图片
        z=0
        for i in range(0,x):
            for j in range(0,y):
                    im.putpixel((i,j),(int(rgb[z]),int(rgb[z+1]),int(rgb[z+2]))) #rgb转化为像素
                    z+=3
        im.save(config.文件目录+'转换后图片.png')
        self.ui.print_echo.append(f"图片生成成功,请查看{config.文件目录}/转换后图片.png")
    except:
        self.ui.print_echo.append("图片生成失败")
    config.清理目录(self)

def jpg高宽爆破(self):
    
    if config.file_type == 'jpg' or 'jpeg' or 'JPG' or 'JPEG':
        self.ui.print_echo.append("正在探测图片宽高,请稍后...")
        config.文件读取(self)
        try:
            img = Image.open(config.file_name)
            宽度 = img.size[0]
            高度 = img.size[1]
            self.ui.print_echo.append("已探测图片宽:{} 高:{}".format(宽度, 高度))
            with open(config.file_name, 'rb') as f:
                content = f.read()
                data=binascii.hexlify(content).decode()
        except:
            self.ui.print_echo.append("图片打开失败")            
        try:    
            高度_data = data.replace(hex2str(宽度, 高度), hex2str(宽度, 高度 * 2))
            宽度_data = data.replace(hex2str(宽度, 高度), hex2str(宽度*2, 高度))
            self.ui.print_echo.append("正在修改图片高度,请稍后...")
            制作图片(高度_data,config.文件目录,'高度')
            self.ui.print_echo.append("正在修改图片宽度,请稍后...")
            制作图片(宽度_data,config.文件目录,'宽度')
            self.ui.print_echo.append(f'修改完毕,请在{config.文件目录}目录中查看')
        except:
            self.ui.print_echo.append("图片宽高修正失败")
    else:
        self.ui.print_echo.append("你打开的不是jpg图片,请检查")
    config.清理目录(self)
def hex2str(width, height):
    width_hex = format(width, '04x')  # 使用format函数将宽度转换为4位十六进制字符串
    height_hex = format(height, '04x')  # 使用format函数将高度转换为4位十六进制字符串
    return height_hex + width_hex  # 返回高度+宽度的组合字符串
def 制作图片(图片,文件目录,类型):
    try:
        with open(文件目录+'/{}放大后.jpg'.format(类型), 'wb') as f:
            pic = binascii.a2b_hex(图片.encode())
            f.write(pic)
    except:
        print('图片生成失败')

def png高宽爆破(self):
    
    file_data = config.文件读取(self)
    try:
        crc32key = zlib.crc32(file_data[12:29]) # 计算crc
        original_crc32 = int(file_data[29:33].hex(), 16)  # 原始crc
        if crc32key == original_crc32:  # 计算crc对比原始crc
            self.ui.print_echo.append("宽高没有问题!")
        else:
            self.ui.print_echo.append("图片宽高被修改,正在解析")
            self.设置进度.emit(1499)
            for i, j in itertools.product(range(1920), range(1080)):  # 理论上0x FF FF FF FF，但考虑到屏幕实际/cpu，0x 0F FF就差不多了，也就是4095宽度和高度
                data = (config.file_data[12:16]+ struct.pack(">i", i)+ struct.pack(">i", j)+ config.file_data[24:29])
                crc32 = zlib.crc32(data)
                self.进度信号.emit(i)
                if crc32 == original_crc32:  # 计算当图片大小为i:j时的CRC校验值，与图片中的CRC比较，当相同，则图片大小已经确定
                    self.ui.print_echo.append(f"\nCRC32: {hex(original_crc32)}")
                    self.ui.print_echo.append(f"正确宽度为: {i}, hex: {hex(i)}")
                    self.ui.print_echo.append(f"正确高度为: {j}, hex: {hex(j)}")
                    # 修改宽高,并生成新的图片保存
                    self.ui.print_echo.append(f"正在生成修改后的图片,保存在:{config.文件目录}png修正生成图.png")
                    image_byte = (
                        config.file_data[:16]
                        + i.to_bytes(4, "big")
                        + j.to_bytes(4, "big")
                        + config.file_data[24:])
                    try:
                        with open(config.文件目录 + "png修正生成图.png", "wb") as f:
                            f.write(image_byte)
                        self.ui.print_echo.append("图片生成成功,请查看")
                        #self.ui.print_echo.append(f"<p><img src={config.文件目录}png修正生成图.png" + "/></p>")
                    except:
                        self.ui.print_echo.append("图片生成失败,请检查路径")
                    
    except:
        self.ui.print_echo.append("图片宽高爆破失败,请检查图片格式")
    config.清理目录(self)

def GIF帧分离(self):
    try:
        im = Image.open(config.file_name)
        iter = ImageSequence.Iterator(im)
        mkdirlambda = lambda x: os.makedirs(x) if not os.path.exists(x) else True
        mkdirlambda(config.文件目录+'gif分离')
        index = 1
        for frame in iter:
            frame.save( f'{config.文件目录}gif分离/frame{index}.png')
            index += 1
        self.ui.print_echo.append("gif图片分离完毕,请打开原图位置查看GIF文件夹")
    except :
        self.ui.print_echo.append("gif图片分离失败")
    config.清理目录(self)

def GIF合并(self):
    
    try:
        imagefiles = os.listdir(config.文件目录)
        imagefiles.sort(key=lambda x:int(x[5:-4]))
        图片组 = []
        for i in imagefiles:
            图片组.append(Image.open(config.文件目录+i))
        # 计算要合并的文件宽高
        单图高 = 图片组[0].height
        单图宽 = 图片组[0].width
        # 创建图片 宽为单图*图数量，高为单图高
        im = Image.new('RGBA',(单图宽*len(imagefiles),单图高))
        width = 0 # 图片宽度
        for 单图 in 图片组:
            im.paste(单图,(width,0,单图宽+width,单图高))
            width = width + 单图宽
        im.save(config.文件目录+'gif合并.png')
        self.ui.print_echo.append(f"gif图片合并成功,请打开原图位置查看 {config.文件目录+'gif合并.png'} 文件")
        im.show()
    except: self.ui.print_echo.append("gif图片合并失败")
    config.清理目录(self)

def 图片逆序(self):
    f1 = open(config.file_name,'rb+')
    f2 = open(config.文件目录+'flag.jpg','wb+')
    f2.write(f1.read()[::-1])
    self.ui.print_echo.append("图片逆序成功,请打开原图位置查看flag.jpg")
    f1.close()
    f2.close()
    config.清理目录(self)

def 黑白图(self):
    imagefiles = os.listdir(config.文件目录)
    imagefiles.sort(key=lambda x:int(x[5:-4]))
    二进制a = []
    二进制b = []
    for i in imagefiles:   
        x = Image.open(config.文件目录+i)
        if x.getcolors()[0][1][0] > 10:
            二进制a.append('1')
            二进制b.append('0')
        elif x.getcolors()[0][1][0] < 10:
            二进制a.append('0')
            二进制b.append('1')
    # 转字符串
    try:
        print (hex(eval(二进制a))[2:-1]).decode('hex')
        print (hex(eval(二进制b))[2:-1]).decode('hex')
    except:
        self.ui.print_echo.append("不能转换为字符串,尝试转换图片")
    try:
        x = y = int(len(二进制a) ** 0.5)
        rgb=[]
        for i in range(0,len(二进制a)):  # 为了保证有rgb颜色,所以只能拓展了
            if 二进制a[i] == '0' :
                rgb.append(255)
                rgb.append(255)
                rgb.append(255)
            elif 二进制a[i] == '1':
                rgb.append(0)
                rgb.append(0)
                rgb.append(0)
        制图(self,x,y,rgb)
    except:
        self.ui.print_echo.append("不能生成图片")
    

#! todo: 16进制转图片
def 进制转图片(self, ttl):
    图像坐标=''
    for ttl_str in ttl:
        图像坐标 += bin(int(ttl_str))[2:].zfill(8) # 将16进制转成二进制,去除0b，并补全到8位
    MAX=int(math.sqrt(len(图像坐标))) # 计算出图片最大边长
    img = Image.new("RGB",(MAX,MAX)) # 创建图片
    i = 0
    for y in range (0,MAX):
        for x in range (0,MAX):
            if(图像坐标[i] == '1'):  # 判断像素点颜色 1为白色，0为黑色 
                img.putpixel([x,y],(0, 0, 0))
            else:
                img.putpixel([x,y],(255,255,255))
            i = i+1
    img.show()

def StegHide(self):
    password = []
    if 'txt' in password :
        with open(config.str1, 'r') as file:
            password = file.readlines()
    else:
        password.append(config.str1)
    for i in password:
        命令 = f'.\\tools\\jiaoben\\steghide\\steghide.exe extract -sf {config.file_name} -p {i} -xf {config.文件目录}flag.txt'
        内容 = subprocess.run(命令)
        print(f'执行完毕，请查看{config.文件目录}flag.txt')


